#pragma once

// 面向对象化封装socket编程的基本流程
// 以模板模式进行封装Socket
// tcp和 udp可以继承
#include <iostream>
#include <string>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"

using namespace LogModule;

namespace SocketModule
{
    // 基类提供方法 ,子类继承
    // 模板方法模式
    const int gbacklog = 8;
    class Socket
    {
    public:
        virtual ~Socket()
        {
        }
        // 创建套接字
        virtual void SocketOrDie()
        {
        }
        // 绑定 IP和端口号
        virtual void BindOrDie(uint16_t port)
        {
        }
        // 切换为listen 状态
        virtual void ListenOrDie(int backlog)
        {
        }
        // TCP服务
        // 创建套接字，绑定，监听
        void BuildTcpMethod(uint16_t port, int backlog=gbacklog)
        {
            SocketOrDie();
            BindOrDie(port);
            ListenOrDie(backlog);
        }
        void BuildTcpClientMethod()
        {
            SocketOrDie();
        }
        // 至此获取到了监听套接字
        // 现在需要开始获取连接
        // 返回一个套接字对象，方便调用其他方法（读和写）
        virtual std::shared_ptr<Socket> Accept(InetAddr* client)
        {
            return nullptr;
        }
        virtual void Close()
        {

        }
        //新增读写接口
        virtual int Recv(std::string* out)=0;
        virtual int Send(std::string& message)=0;
        virtual int Connect(const std::string& server_ip,uint16_t port)=0;
    };


    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int fd) : _sockfd(fd)
        {
        }
        TcpSocket()
        {
        }
        ~TcpSocket()
        {
        }

        void SocketOrDie() override
        {
            _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::FATAL) << "创建套接字失败";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::INFO) << "创建套接字成功";
        }
        void BindOrDie(uint16_t port) override
        {
            InetAddr peer(port);
            int n = ::bind(_sockfd, peer.NetAddrPtr(), peer.NetAddrLen());
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "绑定失败";
                exit(BIND_ERR);
            }
            LOG(LogLevel::INFO) << "绑定成功";
        }

        void ListenOrDie(int backlog) override
        {
            int n = ::listen(_sockfd, backlog);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "监听失败";
                exit(LISTEN_ERR);
            }
            LOG(LogLevel::INFO) << "监听成功";
        }
        // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        std::shared_ptr<Socket> Accept(InetAddr* client) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int fd = ::accept(_sockfd, CONV(peer), &len);
            if (fd < 0)
            {
                LOG(LogLevel::FATAL) << "获取连接失败";
                return nullptr;
            }
            LOG(LogLevel::INFO) << "获取连接成功";
            //将客户端 的信息填充带出去
            client->setAddr(peer);
            return std::make_shared<TcpSocket>(fd);
        }
        void Close() override
        {
            if(_sockfd>=0)
            ::close(_sockfd);
        }
        int Recv(std::string* out) override
        {
            //流式读取 ，不关心返回结果
            char buffer[1024];
            ssize_t n=::recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if(n>0)
            {
                buffer[n]=0;
                *out+=buffer;
            }
            return n;
        }
        int Send(std::string& message) override
        {
            return send(_sockfd,message.c_str(),message.size(),0);
        }
        int Connect(const std::string& server_ip,uint16_t port)override
        {
            InetAddr server(server_ip,port);
            return ::connect(_sockfd,server.NetAddrPtr(),server.NetAddrLen());
        }
    private:
        int _sockfd; // 可能为监听套接字，也可能是普通套接字
    };
}